import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, Component, Input, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { orderBy as _orderBy, filter as _filter } from 'lodash';
import { DatatableComponent } from './datatable.component';
import { DatatableModule } from './datatable.module';

@Component({
  template: `
    <lv-datatable
      lvSelectionMode="multiple"
      lvSort
      #sort="lvSort"
      [lvSortDirct]="sort"
      [lvData]="lvData"
      [lvKeepWhiteSpace]="keepWhiteSpace"
      #lvTable
    >
      <thead>
        <tr>
          <th [lvDisabled]="disabled" [lvShowCheckbox]="lvShowCheckbox" lvCheckboxTip="tipTest" width="40px"></th>
          <th
            id="nameTh"
            [lvShowSort]="lvShowSort"
            [lvShowFilter]="lvShowFilter"
            [(lvFilters)]="nameFilters"
            (lvFilterChange)="filterChange($event)"
            [lvFilterCaseSensitive]="lvFilterCaseSensitive"
            [lvAlign]="lvAlign"
            [lvShowCustom]="lvShowCustom"
            lvCellKey="name"
            [lvFilterAuto]="true"
          >
            Name
            <div lvCustom>
              <i lv-icon="lv-icon-filter"></i>
            </div>
          </th>
          <th
            id="ageTh"
            [lvShowSort]="lvShowSort"
            [lvAlign]="lvAlign"
            [lvShowFilter]="lvShowFilter"
            [lvFilterCheckAll]="lvFilterCheckAll"
            [lvFilterSearch]="lvFilterSearch"
            [lvFilterMultiple]="lvFilterMultiple"
            [(lvFilters)]="ageFilters"
            [lvCustomFilter]="customFilterAge"
            (lvFilterChange)="filterChange($event)"
            lvCellKey="age"
            [lvCustomSort]="customSortAge"
            [lvDisableSortClear]="disableSortClear"
            [lvFilterAuto]="true"
          >
            Age
          </th>
        </tr>
      </thead>
      <tbody>
        <ng-container *ngFor="let item of lvTable.renderData">
          <tr>
            <td style="width: 40px"></td>
            <td>{{ item.name }}</td>
            <td>{{ item.age }}</td>
          </tr>
        </ng-container>
      </tbody>
    </lv-datatable>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestThComponent {
  @ViewChild(DatatableComponent, { static: false }) lvTable: DatatableComponent;
  @Input() lvData = [
    { name: 'user2', age: 11 },
    { name: 'user1', age: 12 },
    { name: 'user3', age: 10 },
  ];

  nameFilters = [
    {
      key: 0,
      label: 'user1',
      value: 'user1',
    },
    {
      key: 1,
      label: 'User1',
      value: 'User1',
    },
  ];

  ageFilters = [
    {
      key: 0,
      label: '10',
      value: '10',
    },
    {
      key: 1,
      label: '11',
      value: '11',
    },
  ];

  @Input() lvAlign = 'left';
  @Input() lvShowSort = false;
  @Input() lvShowCheckbox = false;
  @Input() lvShowCustom = false;
  @Input() lvShowFilter = false;
  @Input() lvFilterSearch = false;
  @Input() lvFilterCheckAll = false;
  @Input() lvFilterMultiple = true;
  @Input() lvFilterCaseSensitive = false;
  @Input() keepWhiteSpace = false;
  disabled = false;
  disableSortClear = false;

  filterChange(options: any) {
    this.lvTable.filter(options);
  }

  customSortAge;
  customFilterAge;
}

describe('ThComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestThComponent],
      imports: [DatatableModule, CommonModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
    }).compileComponents();
  }));
  describe('render', () => {
    let component: TestThComponent;
    let fixture: ComponentFixture<TestThComponent>;
    const renderComp = fakeAsync((para) => {
      for (const [key, value] of Object.entries(para)) {
        component[key] = value;
      }
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
      tick(1500);
    });

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestThComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      tick(500);
    }));

    // 对齐方式渲染
    it('with lvAlign', () => {
      renderComp({ lvAlign: 'center' });
    });

    it('with lvAlign', () => {
      renderComp({ lvAlign: 'right' });
    });

    // 是否展示排序按钮渲染
    it('with lvShowSort', () => {
      renderComp({ lvShowSort: true });
    });

    // 复选框渲染
    it('with lvShowCheckbox and lvDisabled', () => {
      renderComp({ lvShowCheckbox: true, disabled: true });
    });

    // 是否展示过滤按钮渲染
    it('with lvShowFilter', () => {
      renderComp({ lvShowFilter: true });
    });

    // 多空格渲染 lvKeepWhiteSpace
    it('with lvKeepWhiteSpace', () => {
      renderComp({ keepWhiteSpace: true });
    });

    // 表头自定义渲染 lvShowCustom
    it('with lvShowCustom', () => {
      renderComp({ lvShowCustom: true });
    });
  });

  describe('sort', () => {
    let component: TestThComponent;
    let fixture: ComponentFixture<TestThComponent>;

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestThComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      tick(500);
    }));

    // 点击能正常排序（最后一个排序生效）
    it('work correctly when click sort', fakeAsync(() => {
      component.lvShowSort = true;
      fixture.detectChanges();
      const firstName = fixture.nativeElement.querySelectorAll('.lv-table-td-content')[1];
      expect(firstName.textContent).toContain('user2');

      // 点击按照name排序
      const nameHeader = fixture.nativeElement.querySelector('#nameTh');
      nameHeader.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const firstName2 = fixture.nativeElement.querySelectorAll('.lv-table-td-content')[1];
      expect(firstName2.textContent).toContain('user1');
      const firstAge = fixture.nativeElement.querySelectorAll('.lv-table-td-content')[2];
      expect(firstAge.textContent).toContain('12');

      // 点击按照age排序
      const ageHeader = fixture.nativeElement.querySelector('#ageTh');
      ageHeader.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const firstAge2 = fixture.nativeElement.querySelectorAll('.lv-table-td-content')[2];
      expect(firstAge2.textContent).toContain('10');
    }));

    // 自定义排序生效（lvCustomSort）
    it('work correctly with lvCustomSort', fakeAsync(() => {
      component.lvShowSort = true;
      component.customSortAge = (data, sortOptions) => _orderBy(data, [sortOptions.key], ['desc']);
      fixture.detectChanges();

      const ageHeader = fixture.nativeElement.querySelector('#ageTh');
      ageHeader.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      const firstAge = fixture.nativeElement.querySelectorAll('.lv-table-td-content')[2];
      expect(firstAge.textContent).toContain('12');
    }));

    // 禁用清除排序生效（lvDisableSortClear）
    it('work correctly with lvDisableSortClear', fakeAsync(() => {
      component.lvShowSort = true;
      component.disableSortClear = true;
      fixture.detectChanges();

      const clickSort = (expectAge) => {
        const ageHeader = fixture.nativeElement.querySelector('#ageTh');
        ageHeader.dispatchEvent(new Event('click'));
        tick(500);
        fixture.detectChanges();
        const firstAge = fixture.nativeElement.querySelectorAll('.lv-table-td-content')[2];
        expect(firstAge.textContent).toContain(expectAge);
      };

      // 点击排序后升序和降序切换，不会清除排序
      clickSort('10');
      clickSort('12');
      clickSort('10');
    }));

    // 复选框提示信息（lvCheckboxTip）
    it('work correctly with checkbox', fakeAsync(() => {
      component.lvShowCheckbox = true;
      fixture.detectChanges();

      const checkbox = fixture.nativeElement.querySelectorAll('.lv-checkbase');
      checkbox[0].dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
      tick(500);
      fixture.detectChanges();

      expect(document.querySelector('.lv-tooltip-inner').textContent).toContain('tipTest');
    }));

    // 复选框disabled时，点击不生效
    it('work correctly when checkbox disabled', fakeAsync(() => {
      component.lvShowCheckbox = true;
      component.disabled = true;
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      const checkbox = fixture.nativeElement.querySelector('.lv-cell-checkbox');
      checkbox.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      expect(fixture.nativeElement.querySelector('.lv-icon-checked')).toBeNull();
    }));
  });

  describe('filter', () => {
    let component: TestThComponent;
    let fixture: ComponentFixture<TestThComponent>;

    const clickFilterPanel = (thId) => {
      // 点击过滤标签，弹出过滤面板
      const th = fixture.nativeElement.querySelector(thId);
      const filterIcon = th.querySelector('.lv-icon-host');
      filterIcon.dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
    };

    const clickFilterItem = (index) => {
      const filterItem = document.querySelectorAll('.lv-filter-select-item')[index];
      filterItem.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
    };

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestThComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      tick(500);
    }));

    // 单选过滤生效 lvFilterMultiple为false
    it('work correctly when lvFilterMultiple pass false', fakeAsync(() => {
      component.lvShowFilter = true;
      component.lvFilterMultiple = false;
      fixture.detectChanges();
      clickFilterPanel('#ageTh');
      expect(document.activeElement).toMatchSnapshot();

      // 选择第一项，age 10
      clickFilterItem(0);
      const tdContent = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent.length).toEqual(3);
      expect(tdContent[2].textContent).toEqual('10');

      // 选择第二项过滤，age 11
      clickFilterItem(1);
      const tdContent2 = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent2.length).toEqual(3);
      expect(tdContent2[2].textContent).toEqual('11');
    }));

    // 输入能正常过滤（多个过滤项同时生效）(全选lvFilterCheckAll能生效)
    it('work correctly when input filter', fakeAsync(() => {
      component.lvShowFilter = true;
      component.lvFilterCheckAll = true;
      fixture.detectChanges();
      clickFilterPanel('#ageTh');
      expect(document.activeElement).toMatchSnapshot();

      // 选择第一项，age 10
      clickFilterItem(1);
      const tdContent = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent.length).toEqual(3);
      expect(tdContent[2].textContent).toEqual('10');

      // 继续选择第二项过滤，age 11
      clickFilterItem(2);
      const tdContent2 = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent2.length).toEqual(6);

      // 当前已经全选，勾选去全选，三条数据全部显示
      clickFilterItem(0);
      const tdContent3 = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent3.length).toEqual(9);

      // 勾选全选，只显示10 11两条数据
      clickFilterItem(0);
      const tdContent4 = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent4.length).toEqual(6);
    }));

    // 全部数据自定义过滤生效 lvCustomFilter
    it('work correctly with lvCustomFilter', fakeAsync(() => {
      component.lvShowFilter = true;
      component.lvFilterCheckAll = true;
      component.customFilterAge = (data, _) => _filter(data, (item) => item.age === 11);
      fixture.detectChanges();
      clickFilterPanel('#ageTh');

      // 选择第一项，age 10
      clickFilterItem(1);
      const tdContent = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent.length).toEqual(3);
      // 自定义过滤返回age 11
      expect(tdContent[2].textContent).toEqual('11');
    }));

    // 过滤大小写敏感生效 lvFilterCaseSensitive
    it('work correctly with lvFilterCaseSensitive', fakeAsync(() => {
      component.lvFilterCaseSensitive = true;
      component.lvShowFilter = true;
      fixture.detectChanges();
      clickFilterPanel('#nameTh');

      // 选择第2项，User1
      clickFilterItem(1);
      const tdContent = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent.length).toEqual(0);

      // 选择第1项，user1
      clickFilterItem(0);
      const tdContent2 = fixture.nativeElement.querySelectorAll('.lv-table-td-content');
      expect(tdContent2.length).toEqual(3);
    }));
  });
});
